#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
客户端软件
连接到ESP32服务器进行双向TLS通信
"""

import socket
import ssl
import threading
import time
import json
from datetime import datetime
from pathlib import Path

class TLSClient:
    def __init__(self, host='localhost', port=8443):
        self.host = host
        self.port = port
        self.socket = None
        self.running = False
        self.connected = False
        
        # 证书文件路径
        self.cert_file = 'certs/client-cert.pem'
        self.key_file = 'certs/client-key.pem'
        self.ca_file = 'certs/ca-cert.pem'
        
        # 检查证书文件
        self._check_certificates()
        
    def _check_certificates(self):
        """检查证书文件是否存在"""
        required_files = [self.cert_file, self.key_file, self.ca_file]
        for file_path in required_files:
            if not Path(file_path).exists():
                raise FileNotFoundError(f"证书文件不存在: {file_path}")
        print("✓ 证书文件检查通过")
    
    def _create_ssl_context(self):
        """创建SSL上下文"""
        # 创建SSL上下文
        context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
        
        # 加载客户端证书和私钥
        context.load_cert_chain(self.cert_file, self.key_file)
        
        # 设置服务器证书验证
        context.check_hostname = False  # 因为使用localhost
        context.verify_mode = ssl.CERT_REQUIRED
        context.load_verify_locations(self.ca_file)
        
        return context
    
    def connect(self):
        """连接到服务器"""
        try:
            print(f"[{datetime.now().strftime('%H:%M:%S')}] 正在连接到 {self.host}:{self.port}...")
            
            # 创建SSL上下文
            ssl_context = self._create_ssl_context()
            
            # 创建socket并连接
            raw_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.socket = ssl_context.wrap_socket(
                raw_socket, 
                server_hostname=self.host
            )
            
            self.socket.connect((self.host, self.port))
            self.socket.settimeout(1.0)
            
            self.connected = True
            self.running = True
            
            print(f"[{datetime.now().strftime('%H:%M:%S')}] ✓ 连接成功")
            
            # 获取服务器证书信息
            cert = self.socket.getpeercert()
            if cert:
                subject = dict(x[0] for x in cert['subject'])
                print(f"[{datetime.now().strftime('%H:%M:%S')}] 服务器证书: {subject.get('commonName', 'Unknown')}")
            
            # 启动消息接收线程
            receive_thread = threading.Thread(target=self._receive_messages, daemon=True)
            receive_thread.start()
            
            return True
            
        except Exception as e:
            print(f"[{datetime.now().strftime('%H:%M:%S')}] ✗ 连接失败: {e}")
            self.connected = False
            return False
    
    def _receive_messages(self):
        """接收服务器消息"""
        while self.running and self.connected:
            try:
                data = self.socket.recv(1024)
                if not data:
                    print(f"[{datetime.now().strftime('%H:%M:%S')}] 服务器断开连接")
                    break
                
                message = data.decode('utf-8')
                
                # 尝试解析JSON消息
                try:
                    msg_data = json.loads(message)
                    self._handle_server_message(msg_data)
                except json.JSONDecodeError:
                    print(f"[{datetime.now().strftime('%H:%M:%S')}] 服务器消息: {message}")
                    
            except socket.timeout:
                continue
            except Exception as e:
                if self.running:
                    print(f"[{datetime.now().strftime('%H:%M:%S')}] 接收消息错误: {e}")
                break
        
        self.connected = False
    
    def _handle_server_message(self, msg_data):
        """处理服务器消息"""
        msg_type = msg_data.get('type', 'unknown')
        timestamp = msg_data.get('timestamp', '')
        
        if msg_type == 'welcome':
            print(f"[{datetime.now().strftime('%H:%M:%S')}] 🎉 {msg_data.get('message', '')}")
            server_info = msg_data.get('server_info', {})
            if server_info:
                print(f"[{datetime.now().strftime('%H:%M:%S')}] 设备: {server_info.get('device', 'Unknown')}")
                print(f"[{datetime.now().strftime('%H:%M:%S')}] 固件: {server_info.get('firmware', 'Unknown')}")
        
        elif msg_type == 'sensor_data':
            data = msg_data.get('data', {})
            print(f"[{datetime.now().strftime('%H:%M:%S')}] 📊 传感器数据:")
            print(f"    温度: {data.get('temperature', 'N/A')}°C")
            print(f"    湿度: {data.get('humidity', 'N/A')}%")
            print(f"    气压: {data.get('pressure', 'N/A')} hPa")
            print(f"    光照: {data.get('light', 'N/A')}")
        
        elif msg_type == 'status_response':
            print(f"[{datetime.now().strftime('%H:%M:%S')}] 📋 设备状态:")
            print(f"    状态: {msg_data.get('status', 'Unknown')}")
            print(f"    内存: {msg_data.get('memory_free', 'N/A')} bytes")
            print(f"    WiFi信号: {msg_data.get('wifi_signal', 'N/A')} dBm")
        
        elif msg_type == 'control_response':
            action = msg_data.get('action', '')
            result = msg_data.get('result', '')
            message = msg_data.get('message', '')
            status_icon = "✓" if result == 'success' else "✗"
            print(f"[{datetime.now().strftime('%H:%M:%S')}] {status_icon} 控制响应: {action} - {message}")
        
        elif msg_type == 'echo':
            print(f"[{datetime.now().strftime('%H:%M:%S')}] 🔄 回声: {msg_data.get('response', '')}")
        
        elif msg_type == 'pong':
            print(f"[{datetime.now().strftime('%H:%M:%S')}] 🏓 Pong收到")
        
        elif msg_type == 'error':
            print(f"[{datetime.now().strftime('%H:%M:%S')}] ❌ 错误: {msg_data.get('message', '')}")
        
        else:
            print(f"[{datetime.now().strftime('%H:%M:%S')}] 📨 未知消息类型: {msg_type}")
            print(f"    内容: {msg_data}")
    
    def send_message(self, message):
        """发送消息到服务器"""
        if not self.connected:
            print(f"[{datetime.now().strftime('%H:%M:%S')}] ❌ 未连接到服务器")
            return False
        
        try:
            if isinstance(message, dict):
                message = json.dumps(message, ensure_ascii=False)
            
            self.socket.send(message.encode('utf-8'))
            return True
        except Exception as e:
            print(f"[{datetime.now().strftime('%H:%M:%S')}] 发送消息失败: {e}")
            return False
    
    def send_text(self, text):
        """发送文本消息"""
        return self.send_message(text)
    
    def send_ping(self):
        """发送ping消息"""
        ping_msg = {
            'type': 'ping',
            'timestamp': datetime.now().isoformat()
        }
        return self.send_message(ping_msg)
    
    def get_status(self):
        """获取设备状态"""
        status_msg = {
            'type': 'get_status',
            'timestamp': datetime.now().isoformat()
        }
        return self.send_message(status_msg)
    
    def control_led(self, action):
        """控制LED"""
        if action not in ['led_on', 'led_off']:
            print(f"[{datetime.now().strftime('%H:%M:%S')}] ❌ 无效的LED控制动作: {action}")
            return False
        
        control_msg = {
            'type': 'control',
            'action': action,
            'timestamp': datetime.now().isoformat()
        }
        return self.send_message(control_msg)
    
    def disconnect(self):
        """断开连接"""
        print(f"[{datetime.now().strftime('%H:%M:%S')}] 正在断开连接...")
        self.running = False
        self.connected = False
        
        if self.socket:
            try:
                self.socket.close()
            except:
                pass
        
        print(f"[{datetime.now().strftime('%H:%M:%S')}] 已断开连接")
    
    def interactive_mode(self):
        """交互模式"""
        print("\n=== 交互模式 ===")
        print("可用命令:")
        print("  ping          - 发送ping")
        print("  status        - 获取设备状态")
        print("  led_on        - 开启LED")
        print("  led_off       - 关闭LED")
        print("  quit/exit     - 退出")
        print("  其他文本      - 发送文本消息")
        print("")
        
        while self.connected:
            try:
                user_input = input(">>> ").strip()
                
                if not user_input:
                    continue
                
                if user_input.lower() in ['quit', 'exit']:
                    break
                elif user_input.lower() == 'ping':
                    self.send_ping()
                elif user_input.lower() == 'status':
                    self.get_status()
                elif user_input.lower() == 'led_on':
                    self.control_led('led_on')
                elif user_input.lower() == 'led_off':
                    self.control_led('led_off')
                else:
                    self.send_text(user_input)
                    
            except KeyboardInterrupt:
                break
            except EOFError:
                break
        
        self.disconnect()

def main():
    client = TLSClient()
    
    try:
        if client.connect():
            # 等待一下接收欢迎消息
            time.sleep(1)
            
            # 进入交互模式
            client.interactive_mode()
        else:
            print("连接失败，程序退出")
    
    except KeyboardInterrupt:
        print("\n收到中断信号")
    finally:
        client.disconnect()

if __name__ == "__main__":
    main()